package servicios;

import datos.entidades.*;
import datos.gestores.*;
import excepciones.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;


public class ServicioAsignacionViajes {
	private GestorDatosViaje listaViajesSinChofer;
    private GestorDatosViaje listaViajesConChofer;
    private GestorDatosChofer listaChoferes;
    private GestorDatosRutas listaRutas;
    private GestorDatosVehiculo listaVehiculos;
    private GestorDatosCliente listaClientes;
    private final String BASE_DATOS_PATH="data/";
    private final String NOMBRE_ARCHIVO_RUTAS="rutas.xml";
    private final String NOMBRE_ARCHIVO_CHOFERES="choferes.xml";
    private final String NOMBRE_ARCHIVO_CLIENTES="clientes.xml";
    private final double COEFICIENTE_REDUCCION_VELOCIDAD = 0.75;
    private final double HORAS_MAXIMO_CONDUCCION = 8;
    private final double HORAS_MINIMO_DESCANSO = 5;
    
    public ServicioAsignacionViajes () {
    	listaRutas = new GestorDatosRutas(BASE_DATOS_PATH.concat(NOMBRE_ARCHIVO_RUTAS));
        listaChoferes = new GestorDatosChofer(BASE_DATOS_PATH.concat(NOMBRE_ARCHIVO_CHOFERES));
        listaClientes = new GestorDatosCliente(BASE_DATOS_PATH.concat(NOMBRE_ARCHIVO_CLIENTES));
        listaVehiculos = new GestorDatosVehiculo (BASE_DATOS_PATH);
        listaViajesSinChofer = new GestorDatosViaje(); //esta lista no levanta nada de los archivos
        listaViajesConChofer = new GestorDatosViaje(); //esta lista no levanta nada de los archivos
        try {
            //Se levanta la data desde los archivos.
        	listaRutas.loadData();
        	listaChoferes.loadData();
        	listaClientes.loadData();
        	listaVehiculos.loadData();
		} catch (IOException ex) {
			PopUps.mensajeError("Error intentando cargar los datos");
		}
    }

 // ****************************************************************************************************************************************//
 // ** SERVICIOS EXPORTADOS
 // ****************************************************************************************************************************************//    
    
    /**
     * @param idViaje del viaje que se quiere calcular el costo
     * <p>
     * @return Viaje del cual se calculo y seteo el costo
     * <p>
     * Busca el viaje en la lista de viajes con chofer y calculo el costo segun la formula:
     * costoTotal = costoVehiculo1 + costoVehiculo2 + costoKm
     * <p>
     * Los costos individuales se calculan en otras funciones
     */
    public Viaje calcularCostoViaje (int idx) {
    	
    	int i=0;
    	try {
    		i = listaViajesConChofer.indexOf(idx);
    	} catch (ObjetoNoExisteExcepcion e){
    		System.out.print(e.getMessage());
    	}
    	   	Object viaje = (Viaje) listaViajesConChofer.get(i);
	    	double costoTotal, costoVehiculo1 = 0, costoVehiculo2 = 0, costoKm;
			costoKm = this.calcularCostoKm((Viaje)viaje);
	    	Object vehiculo = (Vehiculo) getVehiculoViaje((Viaje)viaje,0);
	    	if (vehiculo != null) costoVehiculo1 = this.costoVehiculo ((Vehiculo)vehiculo, (Viaje)viaje,0);
			vehiculo = getVehiculoViaje((Viaje)viaje,1);	
			if (vehiculo != null) costoVehiculo2 = this.costoVehiculo ((Vehiculo)vehiculo,(Viaje)viaje,1);
			costoTotal = costoVehiculo1 + costoVehiculo2 + costoKm;
	    	((Viaje) viaje).setCosto(costoTotal);
	    	return (Viaje)viaje;
    }
    
    /**
     * @param idViaje
     * <p>
     * @return Viaje con el ingreso calculado y seteado
     * <p>
     * Determinamos que el ingreso en un viaje es el costo mas el 40% del mismo
     */
    public Viaje calcularIngresoViaje (int idx) {
    	int i = 0;
    	try {
    		i = listaViajesConChofer.indexOf(idx);
    	}catch (ObjetoNoExisteExcepcion e){
    		
    	}
    	Object viaje = (Viaje) listaViajesConChofer.get(i);
    	((Viaje)viaje).setIngreso(((Viaje)viaje).getCosto() * 1.40);
    	return (Viaje)viaje;
    }
    
    /**
     * @return double con el ingreso total segun la lista de los viajes
     * <p>
     * Recorro la lista de viajes con chofer (o sea, los confirmados que ya tienen costo e ingreso llenos) 
   	 * y calculo cual es la ganancia total (ingreso - costo)
     * 
     */
    public double calcularResultadoEconomico () {
    	double gananciaTotal = 0;
    	int i;
    	Object viaje;
    	for (i = 0; i < this.listaViajesConChofer.getCantViajes(); i++) {
    		viaje = (Viaje) this.listaViajesConChofer.get(i);
    		gananciaTotal += ((Viaje) viaje).getIngreso() - ((Viaje) viaje).getCosto(); 
    	}
        return gananciaTotal;
    }

    public Viaje asignarChofer (int idx) {
    	Viaje viaje = (Viaje)this.listaViajesSinChofer.get(idx);
    	for (int i = 0 ; i < viaje.getVehiculosElegidos().getCantVehiculos() ; ++i )
    		determinarChoferes(viaje, i);
    	
    	if (idx == listaViajesSinChofer.getCantViajes() - 1) {		// Si me pidio asignar chofer al ultimo viaje, entonces paso los 
    		int cantViajes = listaViajesSinChofer.getCantViajes();	// viajes de la lista sin choferes a la lista con choferes.
    		for (int i = 0 ; i < cantViajes ; ++i ){
    			viaje = (Viaje) listaViajesSinChofer.get(0);
    			viaje.setIdViaje(listaViajesConChofer.getCantViajes()+1);
    			this.listaViajesConChofer.add(viaje);
    			this.listaViajesSinChofer.remove(0);
    		}
    	}
    	return viaje;
    }
    
    /**
     * Recibe una instancia de la clase Viaje se encarga de determinar la mejor combinacion de vehiculos segun las peticiones 
     * que ese viaje tenga. Tendra en cuenta la carga a transportar y el tiempo maximo impuesto para realizar el viaje. Este 
     * algoritmo no discriminara entre vehiculos del mismo tipo, o sea solo combinara tipos de vehiculo. 
     * Ejemplo: Sera lo mismo la combinacion camioneta1 camion1 que la combinacion camioneta2 camion1
     * <p>
     * @param viaje
     * */
    public void determinarVehiculos (Viaje viaje) throws IngresoDatosExcepcion{
    	boolean existeCombinacion, algunaCumpleElTiempo;
    	if (viaje.getCarga() < 100000) {
    	ArrayList < ArrayList <VehiculoCarga>> combinaciones = new ArrayList <ArrayList <VehiculoCarga>> ();
    	List <CombinacionVehiculos> combinacionesContabilizadas = new ArrayList <CombinacionVehiculos> ();
    	GestorDatosVehiculosElegidos ves = new GestorDatosVehiculosElegidos ();
    	existeCombinacion = buscarCombinaciones(combinaciones, viaje);
    	contabilizarCombinaciones(combinaciones, viaje, combinacionesContabilizadas);
    	algunaCumpleElTiempo = seleccionarPorTiempo(combinacionesContabilizadas, viaje);
    	seleccionarPorCosto(combinacionesContabilizadas); // devuelve la combinacion mas economica. Asi q en el mejor de los casos tendra una sola combinacion. 
    	pasarCombinacionesAListaViajesElegidos(combinacionesContabilizadas, ves);
    	viaje.setVehiculosElegidos(ves);
    	if (existeCombinacion)
    		if (!algunaCumpleElTiempo) throw new ViajeImposibleExcepcion("Viaje imposible en tan poco tiempo");
    	}
    	else
    		throw new CargaExcesivaExcepcion("Carga Excesiva");    	
    }

 // ****************************************************************************************************************************************//
 // ** METODOS PRIVADOS
 // ****************************************************************************************************************************************//    
    
    /**
     * 
     * @param viaje
     * @param pos
     * <p>
     * Calcula si es mejor 1 o 2 choferes en cuanto al tiempo en el que un solo chofer tardara
     * en recorrer el trayecto teniendo en cuenta de que no puede manejar mas del tiempo 
     * maximo determinado por {@link HORAS_MAXIMO_CONDUCCION} y que debera descansar un tiempo 
     * equivalente a {@link HORAS_MINIMO_DESCANSO}.
     */
    private void determinarChoferes(Viaje viaje, int pos) {
    	Random rnd = new Random();
    	VehiculoElegido ve = (VehiculoElegido)viaje.getVehiculosElegidos().get(pos);
		if (viaje.getTiempoMaximo() > (HORAS_MAXIMO_CONDUCCION * 60))
	    	if (viaje.getTiempoMaximo() < calcularTiempoViajeConChoferUnico(ve, viaje)) {
				//asignar dos choferes
				ve.setIdChofer1(rnd.nextInt() % 6 +1);
				ve.setIdChofer2(rnd.nextInt() % 6 +1);
			} else {
				//asignar un chofer
				ve.setIdChofer1(rnd.nextInt() % 6 +1);
			}
		else
			//asignar un chofer
			ve.setIdChofer1(5);
		viaje.getVehiculosElegidos().set(pos, ve);
	}
    
    /**
     * @param viaje de donde se debe buscar el vehiculo
     * <p>
     * @param pos (1 o 2 segun se quiera el primer o segundo vehiculo del viaje)
     * <p>
     * @return Vehiculo encontrado
     * <p>
     * Busca el vehiculo segun la posicion pasada en la lista de VehiculosElegidos en el viaje y lo convierte a Vehiculo para devolverlo
     */
    private Vehiculo getVehiculoViaje (Viaje viaje, int pos) {
    	Vehiculo vehiculo = null;
    	VehiculoElegido ve;
    	GestorDatosVehiculosElegidos gdve = viaje.getVehiculosElegidos();
    	if (pos < gdve.getCantVehiculos()){
    		ve = (VehiculoElegido) gdve.get(pos);
    		vehiculo = (Vehiculo) listaVehiculos.get(listaVehiculos.indexOf(ve.getPatente()));
    	}
    	return vehiculo;
    }
    
    /**
     * @param vehiculo del cual se quiere calcular el costo
     * <p>
     * @param viaje al que pertence el vehiculo
     * <p>
     * @param i representa si es el primer o el segundo VehiculoElegido del viaje
     * <p>
     * @return double con el costo del vehiculo en ese viaje
     * <p>
     * Calcula el costo de el/los choferes y el consumo del vehiculo pasado por parametro y los suma
     * Estos calculos se hacen en otrs funciones invocadas
     */
    private double costoVehiculo (Vehiculo vehiculo, Viaje viaje, int i) {
    	double costoVehiculo = 0;
    	double costoVehiculoChofer;
    	double costoVehiculoConsumo;
    	costoVehiculoChofer = this.calcularCostoChofer(vehiculo,getCantChoferes(viaje,i),viaje);
    	costoVehiculoConsumo = this.calcularConsumoVehiculo (vehiculo, viaje);
    	costoVehiculo = costoVehiculoChofer + costoVehiculoConsumo;
    	return costoVehiculo;
    }
    
    /**
     * @param viaje
     * <p>
     * @param pos (1 o 2 segun la ubicacion del vehiculo)
     * <p>
     * @return int con la cantidad de choferes
     * <p>
     * Devuelve la cantidad de choferes dentro del vehiculo en la posicion "pos" dentro de los vechiculos elegidos del viaje pasado por parametro
     */
    private int getCantChoferes (Viaje viaje, int pos){
    	int cant;
    	GestorDatosVehiculosElegidos gdve;
    	VehiculoElegido ve;
    	gdve = viaje.getVehiculosElegidos();
    	ve = (VehiculoElegido)gdve.get(pos);
    	cant = ve.getCantidadChoferes();
    	return cant;
    }
    
    /**
     * @param viaje
     * <p>
     * @return double con el costo total por los km recorridos
     * <p>
     * Calcula el costo total por lo kms recorridos segun la distancia de la ruta, el tipo de ruta y la carga que se tiene
     * Se usa la funcion RESTO (%) para saber la cantidad total de veces que se debe multiplicar cada variable
     */
    private double calcularCostoKm (Viaje viaje) {
    	double costoKm = 0;
    	Object ruta = (Ruta) listaRutas.get(listaRutas.indexOf(viaje.getIdRuta()));
    	int carga = viaje.getCarga();
    	int distancia = ((Ruta)ruta).getDistancia();
    	int x, y;
    	x = carga / 100;
		if (carga % 100 != 0) x++;
    	if (((Ruta)ruta).getInternacional() == true) {
    		y = distancia / 500;
    		if (y % 500 != 0) y++;
    		costoKm = x * y * 25;
    	} else costoKm = x * distancia * 4;
    	return costoKm; 
    }

    /**
     * @param vehiculo
     * @param viaje
     * @return double representando el consumo del vehiculo en el viaje pasado por parametro
     * <p>
     * Calcula el consumo segun el tipo de vehiculo y la distancia del viaje pasado por parametro
     * Overloads {@link calcularConsumoVehiculo(VehiculoCarga, Viaje)}
     */
    private double calcularConsumoVehiculo (Vehiculo vehiculo, Viaje viaje) { 
    	double consumo = 0;
    	Object ruta = (Ruta )listaRutas.get(listaRutas.indexOf(viaje.getIdRuta()));
    	consumo = vehiculo.getConsumoCargado (viaje.getCarga()) * ((Ruta)ruta).getDistancia() * 2.5;
    	return consumo;
    }
    
    /**
     * 
     * @param vehiculoCarga
     * @param viaje
     * @return double representando el consumo del vehiculo en el viaje pasado por parametro.
     * <p>
     * Calcula el consumo segun el tipo de vehiculo y la distancia del viaje pasado por parametro
     * Overloads {@link calcularConsumoVehiculo}
     */
    private double calcularConsumoVehiculo (VehiculoCarga vehiculoCarga, Viaje viaje) { 
    	double consumo = 0;
    	Object ruta = (Ruta)listaRutas.get(listaRutas.indexOf(viaje.getIdRuta()));
    	if (vehiculoCarga.tipoCarga == VehiculoElegido.Carga.MAX) {
    		consumo = ((Vehiculo)this.listaVehiculos.get(vehiculoCarga.idVehiculo)).getConsumoCargaNoEstandar() * ((Ruta)ruta).getDistancia() * 2.5;
    	} else {
    		consumo = ((Vehiculo)this.listaVehiculos.get(vehiculoCarga.idVehiculo)).getConsumoCargaEstandar() * ((Ruta)ruta).getDistancia() * 2.5;
    	}
    	return consumo;
    }
    
    /**
     * @param vehiculo
     * <p>
     * @param cantChoferes
     * <p>
     * @param viaje
     * <p>
     * @return double con el costo del chofer o de los choferes, segun corresponda
     * <p>
     * Calcula el costo del chofer o los choferes asignados al vehiculo y al viaje pasados por parametro
     * Se debe calcular las horas que esta en viaje (segun la velocidad promedio del vehiculo) y las horas que descansa (si corresponde)
     */
    private double calcularCostoChofer (Vehiculo vehiculo, int cantChoferes, Viaje viaje) {
    	double costoChofer = 0;
    	double horas;
    	double horasAux;
    	int velocidad = vehiculo.getVelocidadCargado(viaje.getCarga());
    	Object ruta = (Ruta) listaRutas.get(listaRutas.indexOf(viaje.getIdRuta()));
    	horas = ((Ruta) ruta).getKmBuenEstado() / velocidad + ((Ruta) ruta).getKmMalEstado() / velocidad * 1.25;
    	if (cantChoferes == 2){
    		horasAux = horas;
    		while (horasAux > 8) {
	    		horasAux -= 8;
	    		horas += 5;
    		}
    	}
    	costoChofer = horas * vehiculo.getCostoChofer();
    	return costoChofer;
    }
    
	// ****************************************************************************************************************************************//
	// ** METODOS PRIVADOS ASOCIADOS A DETERMINAR VEHICULOS
	// ****************************************************************************************************************************************//    
    
	/** vehiculoCarga tiene el indice del vehiculo dentro de la lista de vehiculos y ademas 
	 * posee la info para saber si el vehiculo va a carga maxima o standar, que es necesario
	 * para saber el tiempo que tarda el vehiculo ya que de la carga depende la velocidad
	 * que el vehiculo pueda desarrollar.
	 * */
    private int calcularTiempoViaje (VehiculoCarga vehiculoCarga, Viaje viaje) {
    	double tiempo = 0;
    	Object ruta = ((Ruta)listaRutas.get(listaRutas.indexOf(viaje.getIdRuta())));
    	Object ve = ((Vehiculo)listaVehiculos.get(vehiculoCarga.idVehiculo));
    	if (vehiculoCarga.tipoCarga == VehiculoElegido.Carga.STD) {
    		tiempo = (((double)((Ruta)ruta).getKmBuenEstado()) / ((Vehiculo)ve).getVelocidadPromedioCargaEstandar()) * 60;
    		tiempo += (((double)((Ruta)ruta).getKmMalEstado()) / (((Vehiculo)ve).getVelocidadPromedioCargaEstandar() * COEFICIENTE_REDUCCION_VELOCIDAD)) * 60;
    	} else {
    		tiempo = ((double)((Ruta)ruta).getKmBuenEstado()) / ((Vehiculo)ve).getVelocidadPromedioCargaNoEstandar() * 60;
    		tiempo += ((double)((Ruta)ruta).getKmMalEstado()) / (((Vehiculo)ve).getVelocidadPromedioCargaNoEstandar() * COEFICIENTE_REDUCCION_VELOCIDAD) * 60;
    	}
    	return (int) tiempo;
    }
    
    /** vehiculoCarga tiene el indice del vehiculo dentro de la lista de vehiculos y ademas 
	 * posee la info para saber si el vehiculo va a carga maxima o standar, que es necesario
	 * para saber el tiempo que tarda el vehiculo ya que de la carga depende la velocidad
	 * que el vehiculo pueda desarrollar.
	 * */
    private int calcularTiempoViaje (VehiculoElegido vehiculoElegido, Viaje viaje) {
    	double tiempo = 0;
    	Object ruta = ((Ruta)listaRutas.get(listaRutas.indexOf(viaje.getIdRuta())));
    	Object ve = ((Vehiculo)listaVehiculos.get(listaVehiculos.indexOf(vehiculoElegido.getPatente())));
    	if (vehiculoElegido.getCondicionCarga() == VehiculoElegido.Carga.STD) {
    		tiempo = (((double)((Ruta)ruta).getKmBuenEstado()) / ((Vehiculo)ve).getVelocidadPromedioCargaEstandar()) * 60;
    		tiempo += (((double)((Ruta)ruta).getKmMalEstado()) / (((Vehiculo)ve).getVelocidadPromedioCargaEstandar() * COEFICIENTE_REDUCCION_VELOCIDAD)) * 60;
    	} else {
    		tiempo = ((double)((Ruta)ruta).getKmBuenEstado()) / ((Vehiculo)ve).getVelocidadPromedioCargaNoEstandar() * 60;
    		tiempo += ((double)((Ruta)ruta).getKmMalEstado()) / (((Vehiculo)ve).getVelocidadPromedioCargaNoEstandar() * COEFICIENTE_REDUCCION_VELOCIDAD) * 60;
    	}
    	return (int) tiempo;
    }
    
    /** vehiculoCarga tiene el indice del vehiculo dentro de la lista de vehiculos y ademas 
	 * posee la info para saber si el vehiculo va a carga maxima o standar, que es necesario
	 * para saber el tiempo que tarda el vehiculo ya que de la carga depende la velocidad
	 * que el vehiculo pueda desarrollar.
	 * */
    private int calcularTiempoViajeConChoferUnico (VehiculoElegido ve, Viaje vi) {
    	double tiempo;
    	tiempo = (double) this.calcularTiempoViaje(ve,vi) * (this.HORAS_MAXIMO_CONDUCCION / this.HORAS_MINIMO_DESCANSO); 
    	return (int) tiempo ;
    }
    
    private void pasarCombinacionesAListaViajesElegidos(List <CombinacionVehiculos> combinacionesContabilizadas, GestorDatosVehiculosElegidos ves){
    	VehiculoElegido ve;
    	if (combinacionesContabilizadas.size() != 0) {
			for ( int i = 0 ; i < combinacionesContabilizadas.get(0).vehiculosSeleccionados.size() ; ++i) {
				ve = new VehiculoElegido();
				asignarVehiculoElegido(ve, combinacionesContabilizadas.get(0).vehiculosSeleccionados.get(i));
				ves.add(ve);
			}
		}
    }
	    
    private void seleccionarPorCosto(List <CombinacionVehiculos> combinacionesContabilizadas) {
    	int sizeLista= combinacionesContabilizadas.size() - 1;
    	for (int i = 0 ; i < sizeLista ; ++i ) {
    		if (combinacionesContabilizadas.get(0).combustibleConsumido > combinacionesContabilizadas.get(1).combustibleConsumido) {
    			combinacionesContabilizadas.remove(0);
    		} else {
    			combinacionesContabilizadas.remove(1);
    		}
    	}
    }
    
    private boolean seleccionarPorTiempo(List <CombinacionVehiculos> combinacionesContabilizadas, Viaje viaje) {
    	for (int i = 0 ; i < combinacionesContabilizadas.size() ; ++i ) {
    		if (combinacionesContabilizadas.get(i).minutosViaje > viaje.getTiempoMaximo()) {
    			combinacionesContabilizadas.remove(i);
    			--i;
    		}
    	}
    	return combinacionesContabilizadas.size() != 0;
    }
    
    private void asignarVehiculoElegido(VehiculoElegido ve, VehiculoCarga vc) {
    	ve.setCondicionCarga(vc.tipoCarga);
    	ve.setPatente(((Vehiculo)this.listaVehiculos.get(vc.idVehiculo)).getPatente());
    }
    
    
    private void contabilizarCombinaciones(ArrayList < ArrayList <VehiculoCarga>> comb, Viaje viaje, List <CombinacionVehiculos> combinacionesContabilizadas) {
    	CombinacionVehiculos combinacionVehiculos;
    	for (int i = 0 ; i < comb.size() ; ++i) {
    		combinacionVehiculos = new CombinacionVehiculos();
			combinacionVehiculos.vehiculosSeleccionados = comb.get(i);
    		for (int j = 0 ; j < comb.get(i).size() ; ++j) {
    			combinacionVehiculos.combustibleConsumido += calcularConsumoVehiculo(comb.get(i).get(j), viaje);
    			combinacionVehiculos.minutosViaje = combinacionVehiculos.minutosViaje > calcularTiempoViaje(comb.get(i).get(j), viaje) ? combinacionVehiculos.minutosViaje : calcularTiempoViaje(comb.get(i).get(j), viaje);
    		}
    		combinacionesContabilizadas.add(combinacionVehiculos);
    	}
    }

    /**
     * Pre: La lista de vehiculos debera estar ordenada de manera ascendente.
     * <p>
     * Post: si no hay combinacion posible de vehiculos, devuelve {@link comb} vacia.
     * <p>
     * Busca combinaciones de vehiculos, de manera tal de conseguir aquella combinacion que mejor encaje con la carga que se quiera llevar.
     * Lo que hara es recorrer la lista e ira preguntando si el vehiculo cubre la carga deseada y si lo hace lo tomo como vehiculo candidato,
     * sino pasara al siguiente vehiculo.
     * En caso de llegar al ultimo vehiculo y no haber cubierto el total de carga deseada (esto quiere decir que haran falta mas de un 
     * vehiculo) entonces toma ese vehiculo como candidato para la combinacion y decrementa el valor de {@link vehiculosRestantes} que es
     * la variable que se usa como tope para recorrer la lista dentro del FOR, para que en la siguiente iteracion no se tome en cuenta a ese 
     * vehiculo ya que ya ha sido tomado como candidato.
     * Esto se repetira hasta que cargaRestante sea 0 o que se terminen los vehiculos. La primera opcion hara verdadero a cubierto y la 
     * segunda hara verdadero a imposible (ni todos los vehiculos de la empresa son suficientes para cubrir la carga total).   
     * <p>
     * Ejemplo: Un camion y una camioneta en vez de un un camion con acoplado. 
     * La logica de este algoritmo solo tiene en cuenta la carga que se necesita transportar, es decir que desprecia el tiempo.
     * <p>
     * @param ArrayList < ArrayList <Integer>> comb, Viaje v 
     * */
	private void combinacionFirstFit(ArrayList < ArrayList <VehiculoCarga>> comb, Viaje viaje){
		boolean cubierto = false, imposible = false;
		Vehiculo vehiculoAux = null;
		ArrayList <VehiculoCarga> combinacionBestFit = new ArrayList <VehiculoCarga> ();
		int cargaRestante = viaje.getCarga();
		int vehiculosRestantes = GestorDatosVehiculo.getCantVehiculos();
		while (!cubierto && !imposible) {
			for ( int i = 0 ; i < vehiculosRestantes ; ++i ) {
				vehiculoAux = ((Vehiculo)this.listaVehiculos.get(i));
				if (vehiculoAux.getCargaEstandar()>= cargaRestante) {
					cubierto = true;
					combinacionBestFit.add(new VehiculoCarga(i, VehiculoElegido.Carga.STD));
					break;
				} else if (vehiculoAux.getCargaMaxima() >= cargaRestante) {
					cubierto = true;
					combinacionBestFit.add(new VehiculoCarga(i, VehiculoElegido.Carga.MAX));
					break;
				} else if (i == vehiculosRestantes - 1) {
					combinacionBestFit.add(new VehiculoCarga(i, VehiculoElegido.Carga.MAX));
					cargaRestante -= vehiculoAux.getCargaMaxima();
					--vehiculosRestantes;
					break;
				}
			}
			imposible = (combinacionBestFit.size() == GestorDatosVehiculo.getCantVehiculos() && (!cubierto));
		}
		if (!imposible) comb.add(combinacionBestFit);
	}
    
	
	private boolean combinarAscendenteDescendente (ArrayList < ArrayList <VehiculoCarga>> comb, Viaje viaje, int i, boolean ascendente) {
		boolean cubierto = false, imposible = false;
		Vehiculo vehiculoAux;
		ArrayList <VehiculoCarga> combinacionAscendente = new ArrayList <VehiculoCarga> ();
		int cargaRestante = viaje.getCarga();
		while (!cubierto && !imposible) {
			vehiculoAux = ((Vehiculo)this.listaVehiculos.get(i));
			if (vehiculoAux.getCargaEstandar() > cargaRestante) {
				combinacionAscendente.add(new VehiculoCarga(i, VehiculoElegido.Carga.STD));
				cargaRestante -= vehiculoAux.getCargaEstandar();
			} else {
				combinacionAscendente.add(new VehiculoCarga(i, VehiculoElegido.Carga.MAX));
				cargaRestante -= vehiculoAux.getCargaMaxima();
			}
			if (ascendente) 
				i = i == GestorDatosVehiculo.getCantVehiculos() - 1 ? 0 : ++i;
			else 
				i = i == 0 ? GestorDatosVehiculo.getCantVehiculos() - 1 : --i;
			cubierto = (cargaRestante <= 0);
			imposible = (combinacionAscendente.size() == GestorDatosVehiculo.getCantVehiculos() && (!cubierto));
		}
		if (!imposible) comb.add(combinacionAscendente);
		return cubierto;
	}
	
	private void combinacionBestFit (ArrayList < ArrayList <VehiculoCarga>> comb, Viaje viaje) {
		//falta implementacion
	}
	
	private boolean buscarCombinaciones (ArrayList < ArrayList <VehiculoCarga>> comb, Viaje viaje) {
		combinacionBestFit(comb, viaje);
		combinacionFirstFit(comb, viaje);
		for (int i = 0 ;i < GestorDatosVehiculo.getCantVehiculos() - 1 ; ++i) {
			combinarAscendenteDescendente(comb, viaje, i, true);
			combinarAscendenteDescendente(comb, viaje, i, false);
    		while (this.listaVehiculos.get(i).equals(this.listaVehiculos.get(i+1))) {
    			++i;
    		}
    	}
		return comb.size() != 0;
	}
	
// ****************************************************************************************************************************************//
// ** GETTERS Y SETTERS DE LOS ATRIBUTOS DE LA CLASE PRINCIPAL
// ****************************************************************************************************************************************//	

	public void setListaViajesSinChofer(GestorDatosViaje listaViajes) {
		this.listaViajesSinChofer = listaViajes;
	}

	public GestorDatosViaje getListaViajesSinChofer() {
		return listaViajesSinChofer;
    }

	public void setListaViajesConChofer(GestorDatosViaje listaViajesConChofer) {
		this.listaViajesConChofer = listaViajesConChofer;
	}

	public GestorDatosViaje getListaViajesConChofer() {
		return listaViajesConChofer;
	}

	public void setListaChoferes(GestorDatosChofer listaChoferes) {
		this.listaChoferes = listaChoferes;
	}

	public GestorDatosChofer getListaChoferes() {
		return listaChoferes;
	}

	public void setListaRutas(GestorDatosRutas listaRutas) {
		this.listaRutas = listaRutas;
	}

	public GestorDatosRutas getListaRutas() {
		return listaRutas;
	}

	public void setListaVehiculos(GestorDatosVehiculo listaVehiculos) {
		this.listaVehiculos = listaVehiculos;
	}

	public GestorDatosVehiculo getListaVehiculos() {
		return listaVehiculos;
	}

// ****************************************************************************************************************************************//
// ** CLASES INTERNAS PARA RESOLVER LAS EDD NECESARIAS PARA LA LOGICA IMPLEMENTADA.
// ****************************************************************************************************************************************//	
	
	private class CombinacionVehiculos {
		List <VehiculoCarga> vehiculosSeleccionados;
		double combustibleConsumido;
		int minutosViaje;
		
		public CombinacionVehiculos() {
			this.vehiculosSeleccionados = new ArrayList <VehiculoCarga> ();
			this.minutosViaje = 0;
			this.combustibleConsumido = 0;
		}
	}
	
	private class VehiculoCarga {
		int idVehiculo;
		VehiculoElegido.Carga tipoCarga;
		public VehiculoCarga(int i, VehiculoElegido.Carga c) {
			this.idVehiculo = i;
			this.tipoCarga = c;
		}
	}
}

